#include "epoll.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/fcntl.h>
#include <string.h>
#include <errno.h>
#include "buffer.h"
#include "session.h"

int setnonblocking(int fd)
{
	if(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) == -1)
	{
		return -1;
	}
	return 0;
}

CEpoll::CEpoll()
{
	m_IsWorking = false;
}

CEpoll::~CEpoll()
{
}

void CEpoll::Init(u16int Port, u32int MaxConnections, SessionMgr * SessMgr, CLog * Log)
{
	m_Port = Port;
	m_MaxConnections = MaxConnections;
	m_SessionMgr = SessMgr;
	m_Log = Log;
}

bool CEpoll::Startup()
{
	m_Epoll = epoll_create1(0);
	m_ListenFd = socket(AF_INET, SOCK_STREAM, 0);
	if(m_ListenFd <= 0)
	{
		//log err
		m_Log->Error(__FILE__, __LINE__, "Create listen socket fail.");
		return false;
	}

	struct sockaddr_in sin;
	memset(&sin, 0, sizeof(struct sockaddr_in));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(m_Port);
	sin.sin_addr.s_addr = INADDR_ANY;
	
	if(bind(m_ListenFd, (struct sockaddr *)&sin, sizeof(sin)) != 0)
	{
		//log err
		m_Log->Error(__FILE__, __LINE__, "Bind listen socket fail.");
		return false;
	}

	if(listen(m_ListenFd, 32) != 0)
	{
		//log err
		m_Log->Error(__FILE__, __LINE__, "Listen fail.");
		return false;
	}
	AddToEpoll(m_ListenFd, EPOLLIN | EPOLLET);
	//create_thread
	m_IsWorking = true;
}

void CEpoll::MainLoop()
{
	struct epoll_event events[100];
	while(m_IsWorking)
	{
		int nfds = epoll_wait(m_Epoll, events, 100, -1);
		for(int i = 0; i < nfds; ++i)
		{
			Session * pSession = (Session *)events[i].data.ptr;
			if(pSession->GetFd() == m_ListenFd)
			{
				Accept();
			}
			else if(events[i].events & EPOLLIN)
			{
				ReadData(pSession);
			}
		}
	}
}

void CEpoll::Accept()
{
	struct sockaddr_in clientaddr;
	struct epoll_event event;
	memset(&clientaddr, 0, sizeof(struct sockaddr_in));
	size_t clientaddr_len;
	int connfd = accept(m_ListenFd, (struct sockaddr *)&clientaddr, &clientaddr_len);
	if(connfd < 0)
	{
		//err log
		m_Log->Error(__FILE__, __LINE__, "Accept fail.");
		return;
	}

	AddToEpoll(connfd, EPOLLIN | EPOLLET);
}

void CEpoll::ReadData(Session * pSession)
{
	
	Buffer * pBuf = pSession->GetRecvBuf();
	int fd = pSession->GetFd();
	char buffer[1024];
	bool IsDisconnect = false;
	while(1)
	{
		s32int HeadRemain = S32INT_SIZE - pBuf->GetUsed();
		if(HeadRemain > 0)
		{
			s32int count = recv(fd, buffer, HeadRemain, 0);
			if(count == -1)
			{
				if (errno != EAGAIN)
				{
					IsDisconnect = true;
					//log err
					m_Log->Warn(__FILE__, __LINE__, "Socket %d read error", fd);
				}
				break;
			}
			else if(count == 0)
			{
				IsDisconnect = true;
				//disconnect;
				m_Log->Info(__FILE__, __LINE__, "Socket %d disconnect", fd);
				break;
			}
			pBuf->Append(buffer, count);
		}
		else
		{
			s32int BodySize = pBuf->GetBodySize();
			s32int BodyRemain = BodySize - (pBuf->GetUsed() - S32INT_SIZE);
			if(BodyRemain > 0)
			{
				s32int count = recv(fd, buffer, BodyRemain, 0);
				if(count == -1)
				{
					if (errno != EAGAIN)
					{
						//log err
						m_Log->Warn(__FILE__, __LINE__, "Socket %d read error", fd);
						IsDisconnect = true;
					}
					break;
				}
				pBuf->Append(buffer, count);
			}
			else if(BodyRemain == 0)
			{
				pSession->OnRecvPack();
			}
			else
			{
				IsDisconnect = true;
				//log err
				m_Log->Warn(__FILE__, __LINE__, "Socket %d get pack error", fd);
			}
		}
	}

	if(IsDisconnect)
	{
		pSession->OnDisconnect();
	}
}

bool CEpoll::AddToEpoll(int fd, u32int event)
{
	setnonblocking(fd);
	struct epoll_event ev;
	ev.events = event;
	Session * pSess = m_SessionMgr->CreateSession(fd);
	ev.data.ptr = pSess;
	if(epoll_ctl(m_Epoll, EPOLL_CTL_ADD, fd, &ev) == -1)
	{
		m_Log->Warn(__FILE__, __LINE__, "Socket %d add to epoll error", fd);
		return false;
	}
	pSess->OnConnect();
	return true;
}
